/**
 * 
 */
package com.dalonedrow.module.hq.rpg;

import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator;

import com.dalonedrow.engine.sprite.base.SimpleDimension;
import com.dalonedrow.engine.sprite.base.SimplePoint;
import com.dalonedrow.engine.sprite.base.SimpleVector2;
import com.dalonedrow.engine.systems.JOGLErrorHandler;
import com.dalonedrow.globalvariables.constants.hq.GlobalConstants;
import com.dalonedrow.utils.UtilityMethods;

/**
 * @author Donald
 */
public final class UltimaTileMap {
	/** the map of all cells. */
	private HashMap<SimpleVector2, UltimaTileCell>	cells;
	/** the list of rooms on the map. */
	private UltimaTileCell[][]				rooms;
	/**
	 * Creates a new instance of TileMap.java.
	 */
	public UltimaTileMap() {
		cells = new HashMap<SimpleVector2, UltimaTileCell>();
		rooms = new UltimaTileCell[0][];
		roomIsRoom = new boolean[0];
	}
	/**
	 * Adds a cell to the map, overwriting any cells that existed previously at
	 * the same location.
	 * @param cell the {@link UltimaTileCell}
	 */
	public void addCell(final UltimaTileCell cell) {
		cells.put(new SimpleVector2(cell.getX(), cell.getY()), cell);
	}
	private boolean roomIsRoom[];
	public boolean isRoomACorridor(final int roomNumber) {
		return !roomIsRoom[roomNumber];		
	}
	/**
	 * Adds a room to the map.
	 * @param list the list of cells that make up the room
	 */
	public void addRoom(final boolean roomFlag, final UltimaTileCell... list) {
		// find next available index
		int index = -1;
		for (int i = 0; i < rooms.length; i++) {
			if (rooms[i] == null) {
				index = i;
				break;
			}
		}
		if (index > -1) {
			System.out.println("added room "+index+" " + roomFlag);
			rooms[index] = list;
			this.roomIsRoom[index] = roomFlag;
		} else {
			UltimaTileCell[][] dest = new UltimaTileCell[rooms.length + 1][];
			System.arraycopy(rooms, 0, dest, 0, rooms.length);
			dest[rooms.length] = list;
			rooms = dest;
			dest = null;
			System.out.println("2added room "+roomIsRoom.length+" " + roomFlag);
			
			boolean[] bDest = new boolean[roomIsRoom.length + 1];
			System.arraycopy(roomIsRoom, 0, bDest, 0, roomIsRoom.length);
			bDest[roomIsRoom.length] = roomFlag;
			roomIsRoom = bDest;
			bDest = null;
		}
	}
	/**
	 * Determines if two cells are adjacent to one another. Adjacency is defined
	 * as having a common border. If diagonal adjacencies are allowed, then the
	 * cells can share a common corner SimpleVector2.
	 * @param c0 the first cell
	 * @param c1 the second cell
	 * @param allowDiagonal flag indicating that diagonal cells are allowed
	 * @return true if the cells are adjacent; false otherwise
	 */
	public boolean cellsAdjacent(final int c0, final int c1,
			final boolean allowDiagonal) {
		boolean is = false;
		try {
			SimplePoint pt0 = 
					UtilityMethods.getInstance().convertIntToPoint(c0);
			SimplePoint pt1 = 
					UtilityMethods.getInstance().convertIntToPoint(c1);
			int xDiff = (int) Math.abs(pt1.getX() - pt0.getX());
			int yDiff = (int) Math.abs(pt1.getX() - pt0.getY());
			if (allowDiagonal) {
				if (xDiff <= 1
						&& yDiff <= 1) {
					// x and y differences can be 1 at most
					is = true;
				}
			} else {
				if (Math.abs(xDiff - yDiff) == 1) {
					is = true;
				}
			}
		} catch (Exception ex) {
			JOGLErrorHandler.getInstance().fatalError(ex);
		}
		return is;
	}
	public void clear() {
		cells = new HashMap<SimpleVector2, UltimaTileCell>();
	}
	/** Clears all rooms. */
	public void clearRooms() {
		for (int i = 0; i < rooms.length; i++) {
			rooms[i] = null;
		}
	}
	/**
	 * Gets all cells by TerrainType.
	 * @return {@link HashMap}&lt;{@link UltimaTileTerrain}, {@link ArrayList}
	 *         &lt;{@link UltimaTileCell}&gt;&gt;
	 */
	public HashMap<Integer, ArrayList<UltimaTileCell>>
			getAllCellsByType() {
		HashMap<Integer, ArrayList<UltimaTileCell>> map =
				new HashMap<Integer, ArrayList<UltimaTileCell>>();
		Iterator<SimpleVector2> iter = cells.keySet().iterator();
		while (iter.hasNext()) {
			UltimaTileCell cell = cells.get(iter.next());
			if (cell != null) {
				ArrayList<UltimaTileCell> list = map.get(cell.getType());
				if (list == null) {
					list = new ArrayList<UltimaTileCell>();
				}
				list.add(cell);
				map.put(cell.getType(), list);
			}
		}
		return map;
	}
	public UltimaTileCell getCell(final SimplePoint pt) throws Exception {
		return getCell((int) pt.getX(), (int) pt.getY());
	}
	public UltimaTileCell getCell(final int cellInt) throws Exception {
		return getCell(UtilityMethods.getInstance().convertIntToPoint(cellInt));
	}
	/**
	 * Gets a cell at a specific location.
	 * @param x the cell's x-coordinate
	 * @param y the cell's y-coordinate
	 * @return {@link UltimaTileCell}
	 * @throws Exception if an error occurs
	 */
	public UltimaTileCell getCell(final int x, final int y) throws Exception {
		UltimaTileCell cell = null;
		Iterator<SimpleVector2> iter = cells.keySet().iterator();
		while (iter.hasNext()) {
			SimpleVector2 pt = iter.next();
			if (pt.getX() == x
					&& pt.getY() == y) {
				cell = cells.get(pt);
			}
		}
		return cell;
	}
	/**
	 * Gets all cells.
	 * @return Cell[]
	 */
	public UltimaTileCell[] getCells() {
		ArrayList<UltimaTileCell> list = new ArrayList<UltimaTileCell>();
		Iterator<SimpleVector2> iter = cells.keySet().iterator();
		while (iter.hasNext()) {
			list.add(cells.get(iter.next()));
		}
		return list.toArray(new UltimaTileCell[list.size()]);
	}
	/**
	 * Gets a specific cell's tile index.
	 * @param loc the cell location
	 * @return TileIndex
	 */
	public int getCellTileIndex(final SimpleVector2 loc) {
		int index = -1;
		UltimaTileCell cell = cells.get(loc);
		if (cell != null) {
			index = cell.getCornerFlags();
		}
		return index;
	}
	/**
	 * Gets a specific cell's tile type.
	 * @param loc the cell location
	 * @return UltimaTileTerrain
	 */
	public int getCellType(final SimpleVector2 loc) {
		int type = -1;
		UltimaTileCell cell = cells.get(loc);
		if (cell != null) {
			type = cell.getType();
		}
		return type;
	}
	/**
	 * Gets all cells in a specific room.
	 * @param index the room's index
	 * @return {@link UltimaTileCell}[]
	 */
	public UltimaTileCell[] getRoom(final int index) {
		return rooms[index];
	}

	public int[] getRoomsForCell(final int cell) {
		SimplePoint pt = UtilityMethods.getInstance().convertIntToPoint(cell);
		int[] a = getRoomsForCell((int) pt.getX(), (int) pt.getY());
		pt = null;
		return a;
	}
	public int[] getRoomsForCell(final int x, final int y) {
		int[] roomsin = new int[0];
		for (int i = 0; i < rooms.length; i++) {
			if (rooms[i] != null) {
				UltimaTileCell[] list = rooms[i];
				for (int inner = 0; inner < list.length; inner++) {
					if (list[inner].getX() == x
							&& list[inner].getY() == y) {
						int[] dest = new int[roomsin.length + 1];
						System.arraycopy(roomsin, 0, dest, 0, roomsin.length);
						dest[roomsin.length] = i;
						roomsin = dest;
						dest = null;
						break;
					}
				}
			}
		}
		return roomsin;
	}
	/**
	 * Gets the current map size.
	 * @return SimpleDimension
	 */
	public SimpleDimension getSize() {
		int x = 0, y = 0;
		Iterator<SimpleVector2> iter = cells.keySet().iterator();
		while (iter.hasNext()) {
			UltimaTileCell cell = cells.get(iter.next());
			if (cell != null) {
				x = Math.max(x, cell.getX());
				y = Math.max(y, cell.getY());
			}
		}
		return new SimpleDimension(++x, ++y);
	}
	/**
	 * Determines if a specific cell is a secret or closed door.
	 * @param cellInt the integer representing the cell's location
	 * @return true if the cell is a closed or secret door; false otherwise
	 */
	public boolean isCellDoor(final int cellInt) {
		boolean is = false;
		try {
			SimplePoint pt = 
					UtilityMethods.getInstance().convertIntToPoint(cellInt);
			UltimaTileCell cell = cells.get(pt);
			if (cell != null) {
				int closed = HQTerrainTypes.getInstance().getTerrainId(
						"ultima_door_closed_to_floor");
				int secret = HQTerrainTypes.getInstance().getTerrainId(
						"ultima_secret_door_closed_to_floor");
				if (cell.getType() == closed
						|| cell.getType() == secret) {
					is = true;
				}
			}
			pt = null;
			cell = null;
		} catch (Exception ex) {
			JOGLErrorHandler.getInstance().fatalError(ex);
		}
		return is;
	}
	/**
	 * Converts the map to an XML string.
	 * @return String
	 */
	public String toXML() throws Exception {
		int x = getSize().width - 1;
		int y = getSize().height - 1;
		ArrayList<UltimaTileCell> ordered = new ArrayList<UltimaTileCell>();
		ArrayList<String> names = new ArrayList<String>();
		StringBuffer xml = new StringBuffer();
		xml.append("<?xml version=\"1.0\" encoding=\"UTF-8\"?>");
		xml.append(GlobalConstants.NEWLINE);
		xml.append("<TileMap>");
		xml.append(GlobalConstants.NEWLINE);
		Iterator<SimpleVector2> iter = cells.keySet().iterator();
		while (iter.hasNext()) {
			UltimaTileCell cell = cells.get(iter.next());
			if (cell != null) {
				ordered.add(cell);
			}
		}
		Collections.sort(ordered);
		for (int i = 0; i < ordered.size(); i++) {
			UltimaTileCell cell = ordered.get(i);
			if (cell != null) {
				String s =
						HQTerrainTypes.getInstance().getTerrainName(
								cell.getType())
								+ cell.getCornerFlags();
				if (!names.contains(s)) {
					names.add(s);
				}
			}
			xml.append("  <cell>");
			xml.append(GlobalConstants.NEWLINE);
			xml.append("    <x>");
			xml.append(cell.getX());
			xml.append("</x>");
			xml.append("<y>");
			xml.append(cell.getY());
			xml.append("</y>");
			xml.append(GlobalConstants.NEWLINE);
			xml.append("    <type>");
			xml.append(cell.getType());
			xml.append("</type>");
			xml.append("<flags>");
			xml.append(cell.getCornerFlags());
			xml.append("</flags>");
			xml.append(GlobalConstants.NEWLINE);
			xml.append("  </cell>");
			xml.append(GlobalConstants.NEWLINE);
		}
		for (int i = 0; i < rooms.length; i++) {
			if (rooms[i] != null) {
				xml.append("  <room>");
				xml.append(GlobalConstants.NEWLINE);
				for (int j = 0; j < rooms[i].length; j++) {
					UltimaTileCell cell = rooms[i][j];
					xml.append("    <roomcell>");
					xml.append(GlobalConstants.NEWLINE);
					xml.append("      <x>");
					xml.append(cell.getX());
					xml.append("</x>");
					xml.append("<y>");
					xml.append(cell.getY());
					xml.append("</y>");
					xml.append(GlobalConstants.NEWLINE);
					xml.append("    </roomcell>");
					xml.append(GlobalConstants.NEWLINE);
				}
				xml.append("  </room>");
				xml.append(GlobalConstants.NEWLINE);
			}
		}
		xml.append("</TileMap>");
		Collections.sort(names);
		return xml.toString();
	}
}
